#include "util/Util.hpp"

#include <fstream>
#include <iostream>
#include <sstream>

#include "OpenLog.h"

namespace
{
    std::string readShaderSource(const std::string &filename);
} // namespace

GLuint openutil::createShaderProgram(const std::string &vertexShaderFileName, const std::string &fragmentShaderFileName)
{
    LOGI("vertex shader file: %s. fragment shader file: %s", vertexShaderFileName.c_str(), fragmentShaderFileName.c_str());
    GLint vertCompiled;
    GLint fragCompiled;
    GLint linked;

    std::string vshaderSource_cXXString = readShaderSource(std::string(PROJECT_ROOT) + std::string("/glsl/") + vertexShaderFileName);

    std::string fshaderSource_cXXString = readShaderSource(std::string(PROJECT_ROOT) + std::string("/glsl/") + fragmentShaderFileName);

    const GLchar *vshaderSource = vshaderSource_cXXString.c_str();
    const GLchar *fshaderSource = fshaderSource_cXXString.c_str();

    GLuint64 vshader = glCreateShader(GL_VERTEX_SHADER);
    GLuint64 fshader = glCreateShader(GL_FRAGMENT_SHADER);

    /* 将着色器代码加载到着色器句柄中 */
    glShaderSource(vshader, 1, &vshaderSource, NULL);
    glShaderSource(fshader, 1, &fshaderSource, NULL);

    glCompileShader(vshader);
    checkOpenGLError();
    glGetShaderiv(vshader, GL_COMPILE_STATUS, &vertCompiled);
    if (vertCompiled != 1)
    {
        LOGE("Vertex shader compilation failed");
        printShaderLog(vshader);
    }

    glCompileShader(fshader);
    checkOpenGLError();
    glGetShaderiv(fshader, GL_COMPILE_STATUS, &fragCompiled);
    if (fragCompiled != 1)
    {
        LOGE("fragment shader compilation failed");
        printShaderLog(fshader);
    }

    /* 着色器程序 */
    GLuint vfProgram = glCreateProgram();
    /* 将着色器加载到程序中 */
    glAttachShader(vfProgram, vshader);
    glAttachShader(vfProgram, fshader);
    /* 将程序连接到OpenGL中 */
    glLinkProgram(vfProgram);

    checkOpenGLError();
    glGetProgramiv(vfProgram, GL_LINK_STATUS, &linked);
    if (linked != 1)
    {
        LOGE("Program linking failed");
        printProgramLog(vfProgram);
    }

    return vfProgram;
}

void openutil::printShaderLog(GLuint shader)
{
    int len = 0;
    int chWrittn = 0;
    char *log;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
    if (len > 0)
    {
        log = (char *)malloc(len);
        glGetShaderInfoLog(shader, len, &chWrittn, log);
        LOGE("Shader info log: %s", log);
        free(log);
    }
}

void openutil::printProgramLog(int program)
{
    int len = 0;
    int chWrittn = 0;
    char *log;
    glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
    if (len > 0)
    {
        log = (char *)malloc(len);
        glGetProgramInfoLog(program, len, &chWrittn, log);
        LOGE("Shader info log: %s", log);
        free(log);
    }
}

bool openutil::checkOpenGLError()
{
    bool foundError = false;
    int glErr = glGetError();
    while (glErr != GL_NO_ERROR)
    {
        LOGE("OpenGL Error: %s", glErr);
        foundError = true;
        glErr = glGetError();
    }

    return foundError;
}

namespace
{
    std::string readShaderSource(const std::string &filename)
    {
        std::string content;
        std::ifstream file(filename, std::ios::in);
        std::string line = "";
        while (!file.eof())
        {
            std::getline(file, line);
            content += line + "\n";
        }
        file.close();
        return content;
    }
} // namespace
